////////////////////////////////////////////////////////////////////////////////////////////////
// 
//
// Author: Miajun Yao
// Date: 10/19/2012
// 
////////////////////////////////////////////////////////////////////////////////////////////////



#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"

#include <stdio.h>
#include <math.h>
#include <string.h>

#include "ply.h"
#include "shaderSetup.h"
#include "GL/glew.h"
#include "gl/glut.h"
#include "GL/gl.h" 

#define M_PI 3.14159

//	define a vertex
typedef struct Vertex
{
  float location[4]; 
  float normal[4]; 
  float color[4]; 
  void* other;			//	reserved to store other attributes of the ply vertices
  Vertex()
  {
	  location[0] = 0; location[1] = 0; location[2] = 0; location[3] = 1;
	  normal[0] = 1; normal[1] = 0; normal[2] = 0; normal[3] = 0;
	  color[0] = 0.2; color[1] = 0.2; color[2] = 0.8; color[3] = 1;
	  other = NULL;
  }
} Vertex; 

//	define a face
typedef struct Face
{
  unsigned int count;
  unsigned int *vertices;
  void* other;			//	reserved to store other attributes of the ply vertices
} Face;

//	define a geometry shape
typedef struct
{
	int nindices;			//	number of indices
	GLuint vboHandle;		//	VBO that contain interleaved positions and colors 
	GLuint indexVBO;		//	index of the VBO
	//	no need to record the vertices and indices after they are passed to GPU VBO
} Geometry;

Geometry cube, cone, cylinder, sphere, ply;		//	5 reserved geometry, ply is read from a .ply file

enum Animation {NONE, TURN_CLOCK, TURN_COUNTER, FORWARD, BACKWARD, LIFT_LARM, LIFT_RARM};
Animation anim = NONE;
int step = 0;
const int stepNumber = 50;		//	every animation takes 50 steps

//	define sorts of animation and the steps every animation takes.
bool filledOrWired = true;		//	wired or filled mode, press 'w' to switch between them

GLuint programObject;			//	shader program
GLuint c0;						//	position  location
GLuint c1;						//	normal location
GLuint c2;						//	color location
GLuint m0;						//	projectionMatrix location
GLuint m1;						//	modelViewpMatrix location

GLuint m2;						//	lightPos location
GLuint m3;						//	ambientIntensity location
GLuint m4;						//	diffuseIntensity location
GLuint m5;						//	specularIntensity location
GLuint m6;						//	diffuseMaterial location
GLuint m7;						//	specularMaterial location
GLuint m8;						//	shiness location

glm::mat4 modelview;			//	the input modelView Matrix
glm::mat4 projection;			//	the input projection Matrix
glm::mat4 plyModel;				//	ply model transformation
glm::mat4 blobbyManModel = glm::translate(glm::mat4(1.0f), glm::vec3(2.0, 0.2, 0.0));	//	start from (2, 0, 0)

/////////////////////////////////
// glut mouse control 
// 
int xform_mode = 0; 
#define XFORM_NONE    0 
#define XFORM_ROTATE  1
#define XFORM_SCALE 2 

int press_x, press_y; 
int release_x, release_y; 
float x_angle = 0.0; 
float y_angle = 0.0; 
float scale_size = 1; 


//	return the length of a vector
float lengthVector(float* vector, int dimension)
{
	float length = 0;
	for (int i = 0; i < dimension; i++)
		length += vector[i] * vector[i];

	return sqrt(length);
}


//	create a VBO from a PlyFile, if no color specified in the ply file, a default color will be assigned to vertices
void createPly(PlyFile* input)
{
	int i, j;

	// go through the element types
	for(i = 0; i < input->num_elem_types; i = i + 1) 
	{
		int count;
		// setup the element for reading and get the element count
		char* element = setup_element_read_ply(input, i, &count);

		if (!element)
			continue;

		if(strcmp("vertex", element) == 0) 
		{
			Vertex* vertex = new Vertex[count];
			for(j = 0; j < input->elems[i]->nprops; j = j + 1) 
			{
				PlyProperty* property = input->elems[i]->props[j];
				PlyProperty setup;
				if(strcmp("x", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "x";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, location[0]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("y", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "y";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, location[1]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("z", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "z";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, location[2]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("nx", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "nx";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, normal[0]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("ny", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "ny";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, normal[1]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("nz", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "nz";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, normal[2]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("red", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "red";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, color[0]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("green", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "green";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, color[1]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
				else if(strcmp("blue", property->name) == 0 && property->is_list == PLY_SCALAR) 
				{
					setup.name = "blue";
					setup.internal_type = Float32;
					setup.offset = offsetof(Vertex, color[2]);
					setup.count_internal = 0;
					setup.count_offset = 0;
					setup_property_ply(input, &setup);
				}
			}
			get_other_properties_ply(input, offsetof(Vertex,other));

			// find the center and size of the ply model
			float cx = 0, cy = 0, cz = 0;
			float min_x=9999, max_x=-9999, min_y=9999, max_y=-9999; 
			float min_z=9999, max_z=-9999; 

			// copy the data
			for(j = 0; j < count; j++) 
			{
				get_element_ply(input, (void*)(&vertex[j]));
				cx += vertex[j].location[0]; 
				cy += vertex[j].location[1]; 
				cz += vertex[j].location[2];
				if (min_x > vertex[j].location[0]) min_x = vertex[j].location[0]; 
				if (max_x < vertex[j].location[0]) max_x = vertex[j].location[0]; 
				if (min_y > vertex[j].location[1]) min_y = vertex[j].location[1]; 
				if (max_y < vertex[j].location[1]) max_y = vertex[j].location[1]; 
				if (min_z > vertex[j].location[2]) min_z = vertex[j].location[2]; 
				if (max_z < vertex[j].location[2]) max_z = vertex[j].location[2]; 
			}
			cx = cx / (float) count; 
			cy = cy / (float) count; 
			cz = cz / (float) count; 
			float plyScaleX = 2.0 / (max_x - min_x);
			float plyScaleY = 2.0 / (max_y - min_y);
			float plyScaleZ = 2.0 / (max_z - min_z);
			float plyMoveX = -cx;
			float plyMoveY = -min_y;
			float plyMoveZ = -cz;
			
			plyModel = glm::translate(glm::scale(glm::mat4(1.0f), glm::vec3(plyScaleX, plyScaleY, plyScaleZ)), glm::vec3(plyMoveX, plyMoveY, plyMoveZ));

			//	create VBO based on the vertices
			glGenBuffers(1, &ply.vboHandle);						// create an interleaved VBO object
			glBindBuffer(GL_ARRAY_BUFFER, ply.vboHandle);		// bind the first handle 
			glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * count, vertex, GL_STATIC_DRAW); // allocate space and copy the position data over
			glBindBuffer(GL_ARRAY_BUFFER, 0);   // clean up 

			delete []vertex;
		}
		else if(strcmp("face", element) == 0) 
		{
			//	if a face more than 3 vertices, only use the first 3
			ply.nindices = 3 * count; 
			GLuint *indices = new GLuint[ply.nindices]; 
			int offset = 0;

			for(j = 0; j < input->elems[i]->nprops; j = j + 1) 
			{
				PlyProperty* property = input->elems[i]->props[j];
				PlyProperty setup;

				if(strcmp("vertex_indices", property->name) == 0 && property->is_list == PLY_LIST) 
				{
					setup.name = "vertex_indices";
					setup.internal_type = Uint32;
					setup.offset = offsetof(Face, vertices);
					setup.count_internal = Uint32;
					setup.count_offset = offsetof(Face, count);
					setup_property_ply(input, &setup);
				}
				get_other_properties_ply(input, offsetof(Face, other));
				
				// copy the data
				for(j = 0; j < count; j++) 
				{
					Face* face = new Face();
					get_element_ply(input, (void*)(face));
					indices[offset++] = face->vertices[0];
					indices[offset++] = face->vertices[1];
					indices[offset++] = face->vertices[2];
				}
			}
			glGenBuffers(1, &ply.indexVBO); 
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ply.indexVBO); 
			glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * ply.nindices, indices, GL_STATIC_DRAW);  // load the index data 
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);				// clean up 

			delete[] indices;
		}

	}


}

//	create a sphere mesh and create a VBO based on that
void createSphere(float radius, int slices, int stacks, float r, float g, float b) 
{
	int nvertices = slices * (stacks + 1);					//	number of sample vertices
	Vertex* vertex = new Vertex[nvertices];					

	float angleUnitSlice = 2 * M_PI/(float)(slices - 1);	//	angle step between two slices
	float angleUnitStack = M_PI/(float)stacks;				//	angle step between two stacks

	//	set up every vertex
	for (int j = 0; j <= stacks; j++)
	{
		float angleStack = (-M_PI / 2) + angleUnitStack * j; 
		for (int i = 0; i < slices; i++) 
		{
			float angleSlice = angleUnitSlice * i; 

			float z = radius * sin(angleStack);
			float xy = radius * cos(angleStack);
			float x = xy * cos(angleSlice);
			float y = xy * sin(angleSlice);
			
			//	GL_NORMALIZE is enabled so no need to nomalize the normal vector
			
			int count = j * slices + i;
			vertex[count].location[0] = x; 
			vertex[count].location[1] = y;
			vertex[count].location[2] = z;
			vertex[count].location[3] = 1.0;
			
			vertex[count].normal[0] = x; 
			vertex[count].normal[1] = y; 
			vertex[count].normal[2] = z; 
			vertex[count].normal[3] = 0.0;

			vertex[count].color[0] = r; 
			vertex[count].color[1] = g; 
			vertex[count].color[2] = b; 
			vertex[count].color[3] = 1.0;
		}
	}

	// set up the index array 
	sphere.nindices = stacks * 2 * (slices+1); 
	GLuint *indices = new GLuint[sphere.nindices]; 
	int n = 0; 
	for (int j = 0; j < stacks; j++)
		for (int i = 0; i <= slices; i++) 
		{
			int mi = i % slices;  
			int idx = j * slices + mi;						// mesh[j][mi] 
			int idx2 = (j+1) * slices + mi; 
			indices[n++] = idx; 
			indices[n++] = idx2; 
		}


	//	create VBO based on the vertices and indices
	glGenBuffers(1, &sphere.vboHandle);						// create an interleaved VBO object
	glBindBuffer(GL_ARRAY_BUFFER, sphere.vboHandle);		// bind the first handle 
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * nvertices, vertex, GL_STATIC_DRAW); // allocate space and copy the position data over
	glBindBuffer(GL_ARRAY_BUFFER, 0);   // clean up 

	glGenBuffers(1, &sphere.indexVBO); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphere.indexVBO); 
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * sphere.nindices, indices, GL_STATIC_DRAW);  // load the index data 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);				// clean up 

	delete[] vertex;
	delete[] indices;
}

//	create a Cylinder mesh and create a VBO based on that
void createCylinder(float baseRadius, float topRadius, float height, int slices, int stacks, float r, float g, float b) 
{
	int nvertices = slices * stacks;					//	number of sample vertices
	Vertex* vertex = new Vertex[nvertices];					

	float angleUnit = 2 * M_PI/(float)(slices - 1);		//	angle step between two slices

	//	set up every vertex
	for (int j = 0; j < stacks; j++)
		for (int i = 0; i < slices; i++) 
		{
			float angle = angleUnit * i; 
			float fullBaseX = cos(angle) * baseRadius;
			float fullBaseY = sin(angle) * baseRadius;
			float fullTopX = cos(angle) * topRadius;
			float fullTopY = sin(angle) * topRadius;

			float fullZ = height;

			float rate = j / (float)stacks;
			float pos[] = {fullTopX * rate + fullBaseX * (1 - rate), fullTopY * rate + fullBaseY * (1 - rate), fullZ * rate};
			
			float deltX = fullBaseX - fullTopX;
			float deltY = fullBaseY - fullTopY;
			float deltZ = -fullZ;
			float vector[] = {deltX, deltY, deltZ};			
			float length = lengthVector(vector, 3);
			float lengthRate = deltZ * deltZ / length;
			float normal[] = {fullTopX + lengthRate * deltX, fullTopY + lengthRate * deltY, fullZ + lengthRate * deltZ};
			
			//	GL_NORMALIZE is enabled so no need to nomalize the normal vector
			
			int count = j * slices + i;
			vertex[count].location[0] = pos[0]; 
			vertex[count].location[1] = pos[1];
			vertex[count].location[2] = pos[2];
			vertex[count].location[3] = 1.0;
			
			vertex[count].normal[0] = normal[0]; 
			vertex[count].normal[1] = normal[1]; 
			vertex[count].normal[2] = normal[2]; 
			vertex[count].normal[3] = 0.0;

			vertex[count].color[0] = r; 
			vertex[count].color[1] = g; 
			vertex[count].color[2] = b; 
			vertex[count].color[3] = 1.0;
		}

	// set up the index array 
	cylinder.nindices = (stacks-1) * 2 * (slices+1); 
	GLuint *indices = new GLuint[cylinder.nindices]; 
	int n = 0; 
	for (int j = 0; j < stacks-1; j++)
		for (int i = 0; i <= slices; i++) 
		{
			int mi = i % slices;  
			int idx = j * slices + mi;						// mesh[j][mi] 
			int idx2 = (j+1) * slices + mi; 
			indices[n++] = idx; 
			indices[n++] = idx2; 
		}


	//	create VBO based on the vertices and indices
	glGenBuffers(1, &cylinder.vboHandle);						// create an interleaved VBO object
	glBindBuffer(GL_ARRAY_BUFFER, cylinder.vboHandle);		// bind the first handle 
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * nvertices, vertex, GL_STATIC_DRAW); // allocate space and copy the position data over
	glBindBuffer(GL_ARRAY_BUFFER, 0);   // clean up 

	glGenBuffers(1, &cylinder.indexVBO); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cylinder.indexVBO); 
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * cylinder.nindices, indices, GL_STATIC_DRAW);  // load the index data 

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);				// clean up 

	delete[] vertex;
	delete[] indices;
}

//	create a cone mesh and create a VBO based on that
void createCone(float radius, float height, int slices, int stacks, float r, float g, float b) 
{
	int nvertices = slices * stacks;					//	number of sample vertices
	Vertex* vertex = new Vertex[nvertices];					

	float angleUnit = 2 * M_PI/(float)(slices - 1);		//	angle step between two slices

	//	set up every vertex
	for (int j = 0; j < stacks; j++)
		for (int i = 0; i < slices; i++) 
		{
			float angle = angleUnit * i; 
			float fullX = cos(angle) * radius;
			float fullY = sin(angle) * radius;
			float fullZ = height;

			float rate = j / (float)stacks;
			float pos[] = {fullX * rate, fullY * rate, fullZ * rate};
			
			float vector[] = {fullX, fullY, -fullZ};			
			float length = lengthVector(vector, 3);
			float lengthRate = fullZ * fullZ / length;
			float normal[] = {lengthRate * fullX, lengthRate * fullY, fullZ + lengthRate * (-fullZ)};
			
			//	GL_NORMALIZE is enabled so no need to nomalize the normal vector
			
			int count = j * slices + i;
			vertex[count].location[0] = pos[0]; 
			vertex[count].location[1] = pos[1];
			vertex[count].location[2] = pos[2];
			vertex[count].location[3] = 1.0;
			
			vertex[count].normal[0] = normal[0]; 
			vertex[count].normal[1] = normal[1]; 
			vertex[count].normal[2] = normal[2]; 
			vertex[count].normal[3] = 0.0;

			vertex[count].color[0] = r; 
			vertex[count].color[1] = g; 
			vertex[count].color[2] = b; 
			vertex[count].color[3] = 1.0;
		}

	// set up the index array 
	cone.nindices = (stacks-1) * 2 * (slices+1); 
	GLuint *indices = new GLuint[cone.nindices]; 
	int n = 0; 
	for (int j = 0; j < stacks-1; j++)
		for (int i = 0; i <= slices; i++) 
		{
			int mi = i % slices;  
			int idx = j * slices + mi;						// mesh[j][mi] 
			int idx2 = (j+1) * slices + mi; 
			indices[n++] = idx; 
			indices[n++] = idx2; 
		}


	//	create VBO based on the vertices and indices
	glGenBuffers(1, &cone.vboHandle);						// create an interleaved VBO object
	glBindBuffer(GL_ARRAY_BUFFER, cone.vboHandle);		// bind the first handle 
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * nvertices, vertex, GL_STATIC_DRAW); // allocate space and copy the position data over
	glBindBuffer(GL_ARRAY_BUFFER, 0);   // clean up 

	glGenBuffers(1, &cone.indexVBO); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cone.indexVBO); 
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * cone.nindices, indices, GL_STATIC_DRAW);  // load the index data 

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);				// clean up 

	delete[] vertex;
	delete[] indices;
}

//	create a cube mesh and create a VBO based on that
void createCube(float size, float r, float g, float b) 
{
	int nvertices = 24;										//	every vertex may have 3 normals with different facings

	Vertex* vertex = new Vertex[nvertices];		
	
	float vertexData[] = 
	{
		//	every line is a vertex with coordinate, normal and color
		size, size, size, 1.0,  1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,
		size, -size, size, 1.0,  1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,
		size, -size, -size, 1.0,  1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,
		size, size, -size, 1.0,  1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,

		size, size, size, 1.0,  0.0, 1.0, 0.0, 0.0,  r, g, b, 1.0,
		size, size, -size, 1.0,  0.0, 1.0, 0.0, 0.0,  r, g, b, 1.0,
		-size, size, -size, 1.0,  0.0, 1.0, 0.0, 0.0,  r, g, b, 1.0,
		-size, size, size, 1.0,  0.0, 1.0, 0.0, 0.0,  r, g, b, 1.0,

		size, size, size, 1.0,  0.0, 0.0, 1.0, 0.0,  r, g, b, 1.0,
		-size, size, size, 1.0,  0.0, 0.0, 1.0, 0.0,  r, g, b, 1.0,
		-size, -size, size, 1.0,  0.0, 0.0, 1.0, 0.0,  r, g, b, 1.0,
		size, -size, size, 1.0,  0.0, 0.0, 1.0, 0.0,  r, g, b, 1.0,

		-size, -size, -size, 1.0,  -1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,
		-size, -size, size, 1.0,  -1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,
		-size, size, size, 1.0,  -1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,
		-size, size, -size, 1.0, -1.0, 0.0, 0.0, 0.0,  r, g, b, 1.0,

		-size, -size, -size, 1.0,  0.0, -1.0, 0.0, 0.0,  r, g, b, 1.0,
		size, -size, -size, 1.0,  0.0, -1.0, 0.0, 0.0,  r, g, b, 1.0,
		size,- size, size, 1.0,  0.0, -1.0, 0.0, 0.0,  r, g, b, 1.0,
		-size, -size, size, 1.0,  0.0, -1.0, 0.0, 0.0,  r, g, b, 1.0,

		-size, -size, -size, 1.0,  0.0, 0.0, -1.0, 0.0,  r, g, b, 1.0,
		-size, size, -size, 1.0,  0.0, 0.0, -1.0, 0.0,  r, g, b, 1.0,
		size, size, -size, 1.0,  0.0, 0.0, -1.0, 0.0,  r, g, b, 1.0,
		size, -size, -size, 1.0,  0.0, 0.0, -1.0, 0.0,  r, g, b, 1.0,
	};

	for (int i = 0; i < nvertices; i++)
	{
		int rowSize = 12;
		int offset = i*rowSize;
		vertex[i].location[0] = vertexData[offset];vertex[i].location[1] = vertexData[offset+1];vertex[i].location[2] = vertexData[offset+2];vertex[i].location[3] = vertexData[offset+3];offset+=4;
		vertex[i].normal[0] = vertexData[offset];vertex[i].normal[1] = vertexData[offset+1];vertex[i].normal[2] = vertexData[offset+2];vertex[i].normal[3] = vertexData[offset+3];offset+=4;
		vertex[i].color[0] = vertexData[offset];vertex[i].color[1] = vertexData[offset+1];vertex[i].color[2] = vertexData[offset+2];vertex[i].color[3] = vertexData[offset+3];
		//	vertex[i].other = NULL;
	}

	//	set up indices
	int index[24];
	for (int i = 0; i < 24; i++)
		index[i] = i;
	cube.nindices = 24;

	//	create VBO based on the vertices and indices
	glGenBuffers(1, &cube.vboHandle);									// create an interleaved VBO object
	glBindBuffer(GL_ARRAY_BUFFER, cube.vboHandle);						// bind the first handle 
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * nvertices, vertex, GL_STATIC_DRAW);	// allocate space and copy the position data over
	glBindBuffer(GL_ARRAY_BUFFER, 0);   // clean up 

	glGenBuffers(1, &cube.indexVBO); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube.indexVBO); 
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index), index, GL_STATIC_DRAW);  // load the index data 

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);  // clean up 
	delete[] vertex;
}

//	bind the cube mesh buffer and draw a cube
void drawCube()
{
	glBindBuffer(GL_ARRAY_BUFFER, cube.vboHandle); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cube.indexVBO); 
	glVertexAttribPointer(c0,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, location[0])); 
	glVertexAttribPointer(c1,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, normal[0])); 
	glVertexAttribPointer(c2,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, color[0])); 
	glDrawElements(GL_QUADS, cube.nindices, GL_UNSIGNED_INT, (char*) NULL+0); 
}

//	bind the cone mesh buffer and draw a cube
void drawCone()
{
	glBindBuffer(GL_ARRAY_BUFFER, cone.vboHandle); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cone.indexVBO); 
	glVertexAttribPointer(c0,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, location[0])); 
	glVertexAttribPointer(c1,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, normal[0])); 
	glVertexAttribPointer(c2,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, color[0])); 
	glDrawElements(GL_TRIANGLE_STRIP, cone.nindices, GL_UNSIGNED_INT, (char*) NULL+0); 
}

//	bind the cylinder mesh buffer and draw a cylinder
void drawCylinder()
{
	glBindBuffer(GL_ARRAY_BUFFER, cylinder.vboHandle); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cylinder.indexVBO); 
	glVertexAttribPointer(c0,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, location[0])); 
	glVertexAttribPointer(c1,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, normal[0])); 
	glVertexAttribPointer(c2,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, color[0])); 
	glDrawElements(GL_TRIANGLE_STRIP, cylinder.nindices, GL_UNSIGNED_INT, (char*) NULL+0); 
}

//	bind the sphere mesh buffer and draw a cylinder
void drawSphere()
{
	glBindBuffer(GL_ARRAY_BUFFER, sphere.vboHandle); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphere.indexVBO); 
	glVertexAttribPointer(c0,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, location[0])); 
	glVertexAttribPointer(c1,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, normal[0])); 
	glVertexAttribPointer(c2,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, color[0])); 
	glDrawElements(GL_TRIANGLE_STRIP, sphere.nindices, GL_UNSIGNED_INT, (char*) NULL+0); 
}

//	draw a PLY object
void drawPly()
{
	//	set the material reflection property(diffuse, specular, shiness)
	glm::vec3 tmp = glm::vec3(0.6f, 0.4f, 0.2f);
	glUniform3fv(m6, 1, &tmp[0]);
	tmp = glm::vec3(0.9f, 0.5f, 0.2f);
	glUniform3fv(m7, 1, &tmp[0]);
	float tmp2 = 10;
	glUniform1f(m8, tmp2);

	glm::mat4 matrix = modelview * plyModel;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 
	glBindBuffer(GL_ARRAY_BUFFER, ply.vboHandle); 
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ply.indexVBO); 
	glVertexAttribPointer(c0,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, location[0])); 
	glVertexAttribPointer(c1,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, normal[0])); 
	glVertexAttribPointer(c2,4,GL_FLOAT, GL_FALSE, sizeof(Vertex),(void*)offsetof(Vertex, color[0])); 
	glDrawElements(GL_TRIANGLES, ply.nindices, GL_UNSIGNED_INT, (char*) NULL+0); 
}

//	draw a floor using cube
void drawFloor()
{
	//	set the material reflection property(diffuse, specular, shiness)
	glm::vec3 tmp = glm::vec3(0.6f, 0.6f, 0.8f);
	glUniform3fv(m6, 1, &tmp[0]);		//	
	tmp = glm::vec3(0.2f, 0.2f, 0.2f);
	glUniform3fv(m7, 1, &tmp[0]);
	float tmp2 = 10;
	glUniform1f(m8, tmp2);

	//	floor model transformation
	glm::mat4 floorModel = glm::translate(glm::scale(glm::mat4(1.0f),  glm::vec3(50, 0.2, 50)), glm::vec3(0, -0.1, 0));
	glm::mat4 matrix = modelview * floorModel;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 
	drawCube();
}

void drawStupidMan()
{
	//	set the material reflection property(diffuse, specular, shiness)
	glm::vec3 tmp = glm::vec3(0.4f, 0.6f, 0.2f);
	glUniform3fv(m6, 1, &tmp[0]);
	tmp = glm::vec3(0.5f, 0.9f, 0.2f);
	glUniform3fv(m7, 1, &tmp[0]);
	float tmp2 = 10;
	glUniform1f(m8, tmp2);

	glm::mat4 sphereModel = glm::translate(glm::mat4(1.0f),  glm::vec3(-2.0, 0.5, 0.0));
	glm::mat4 matrix = modelview * sphereModel;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 
	drawSphere();

	//	set the material reflection property(diffuse, specular, shiness)
	tmp = glm::vec3(0.3f, 0.6f, 0.8f);
	glUniform3fv(m6, 1, &tmp[0]);
	tmp = glm::vec3(0.4f, 0.6f, 0.8f);
	glUniform3fv(m7, 1, &tmp[0]);
	tmp2 = 10;
	glUniform1f(m8, tmp2);

	glm::mat4 coneModel = glm::rotate(glm::translate(glm::mat4(1.0f),  glm::vec3(-2.0, 1.8, 0.0)), 90.0f, glm::vec3(1.0, 0.0, 0.0));
	matrix = modelview * coneModel;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 
	drawCone();
}

void drawBobbyMan()
{
	//	set the material reflection property(diffuse, specular, shiness)
	glm::vec3 tmp = glm::vec3(0.7f, 0.7f, 0.2f);
	glUniform3fv(m6, 1, &tmp[0]);
	tmp = glm::vec3(0.8f, 0.8f, 0.2f);
	glUniform3fv(m7, 1, &tmp[0]);
	float tmp2 = 10;
	glUniform1f(m8, tmp2);

	glm::mat4 model;
	glm::mat4 matrix;
	glm::mat4 modelWhole;

//	left leg
	modelWhole = glm::translate(blobbyManModel, glm::vec3(-0.2, 2.0, 0));
	if (anim == TURN_CLOCK || anim == TURN_COUNTER || anim == FORWARD || anim == BACKWARD)
	{
		float openAngle = ((stepNumber/2 - abs(step - stepNumber/2)) / (float)stepNumber) * 15;
		modelWhole = glm::rotate(modelWhole, openAngle, glm::vec3(1.0, 0, 0));
	}
	//	 left thigh
	model = glm::translate(modelWhole, glm::vec3(0, -0.5, 0.0));
	model = glm::scale(model, glm::vec3(0.3, 1.0, 0.3));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left calf
	model = glm::translate(modelWhole, glm::vec3(0, -1.5, 0.0));
	model = glm::scale(model, glm::vec3(0.25, 1.0, 0.25));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left foot
	model = glm::translate(modelWhole, glm::vec3(0, -2.0, 0.12));
	model = glm::scale(model, glm::vec3(0.2, 0.1, 0.4));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();

//	right leg
	modelWhole = glm::translate(blobbyManModel, glm::vec3(0.2, 2.0, 0));
	if (anim == TURN_CLOCK || anim == TURN_COUNTER || anim == FORWARD || anim == BACKWARD)
	{
		float openAngle = ((stepNumber/2 - abs(step - stepNumber/2)) / (float)stepNumber) * 15;
		modelWhole = glm::rotate(modelWhole, -openAngle, glm::vec3(1.0, 0, 0));
	}
	//	 left thigh
	model = glm::translate(modelWhole, glm::vec3(0, -0.5, 0.0));
	model = glm::scale(model, glm::vec3(0.3, 1.0, 0.3));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left calf
	model = glm::translate(modelWhole, glm::vec3(0, -1.5, 0.0));
	model = glm::scale(model, glm::vec3(0.25, 1.0, 0.25));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left foot
	model = glm::translate(modelWhole, glm::vec3(0, -2.0, 0.12));
	model = glm::scale(model, glm::vec3(0.2, 0.1, 0.4));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();

//	 lower body
	model = glm::translate(blobbyManModel, glm::vec3(0, 2.0, 0.0));
	model = glm::scale(model, glm::vec3(0.6, 0.4, 0.4));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	
//	 higher body
	model = glm::translate(blobbyManModel, glm::vec3(0, 2.8, 0.0));
	model = glm::scale(model, glm::vec3(0.7, 1.4, 0.5));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	
//	 shoulder, center(0, 3.5, 0.0), radius(0.5, 0.2, 0.2)
	model = glm::translate(blobbyManModel, glm::vec3(0, 3.5, 0.0));
	model = glm::scale(model, glm::vec3(1.0, 0.4, 0.4));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();

//	 neck, center(0, 3.8, 0.0), radius(0.1, 0.2, 0.1)
	model = glm::translate(blobbyManModel, glm::vec3(0, 3.8, 0.0));
	model = glm::scale(model, glm::vec3(0.2, 0.3, 0.2));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();

//	 head
	model = glm::translate(blobbyManModel, glm::vec3(0, 4.1, 0.0));
	model = glm::scale(model, glm::vec3(0.4, 0.6, 0.4));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();

//	left arm
	modelWhole = glm::translate(blobbyManModel, glm::vec3(-0.5, 3.5, 0));
	if (anim == TURN_CLOCK || anim == TURN_COUNTER || anim == FORWARD || anim == BACKWARD)
	{
		float openAngle = ((stepNumber/2 - abs(step - stepNumber/2)) / (float)stepNumber) * 15;
		modelWhole = glm::rotate(modelWhole, -openAngle, glm::vec3(1.0, 0, 0));
	}
	else if (anim == LIFT_LARM)
	{
		float openAngle = ((stepNumber/2 - abs(step - stepNumber/2)) / (float)stepNumber) * 90;
		modelWhole = glm::rotate(modelWhole, -openAngle, glm::vec3(1.0, 0, 0));
	}

	//	left upper arm
	model = glm::translate(modelWhole, glm::vec3(0, -0.4, 0.0));
	model = glm::scale(model, glm::vec3(0.25, 0.8, 0.2));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left lower arm
	model = glm::translate(modelWhole, glm::vec3(0, -1.1, 0.0));
	model = glm::scale(model, glm::vec3(0.2, 0.7, 0.18));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left hand
	model = glm::translate(modelWhole, glm::vec3(0, -1.5, 0.0));
	model = glm::scale(model, glm::vec3(0.15, 0.2, 0.15));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();

//	right arm
	modelWhole = glm::translate(blobbyManModel, glm::vec3(0.5, 3.5, 0));
	if (anim == TURN_CLOCK || anim == TURN_COUNTER || anim == FORWARD || anim == BACKWARD)
	{
		float openAngle = ((stepNumber/2 - abs(step - stepNumber/2)) / (float)stepNumber) * 15;
		modelWhole = glm::rotate(modelWhole, openAngle, glm::vec3(1.0, 0, 0));
	}
	else if (anim == LIFT_RARM)
	{
		float openAngle = ((stepNumber/2 - abs(step - stepNumber/2)) / (float)stepNumber) * 90;
		modelWhole = glm::rotate(modelWhole, -openAngle, glm::vec3(1.0, 0, 0));
	}
	//	left upper arm
	model = glm::translate(modelWhole, glm::vec3(0, -0.4, 0.0));
	model = glm::scale(model, glm::vec3(0.25, 0.8, 0.2));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left lower arm
	model = glm::translate(modelWhole, glm::vec3(0, -1.1, 0.0));
	model = glm::scale(model, glm::vec3(0.2, 0.7, 0.18));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
	//	 left hand
	model = glm::translate(modelWhole, glm::vec3(0, -1.5, 0.0));
	model = glm::scale(model, glm::vec3(0.15, 0.2, 0.15));
	matrix = modelview * model;
	glUniformMatrix4fv(m1, 1, GL_FALSE, &matrix[0][0]); 	
	drawSphere();
}

void display() 
{ 
	glEnable(GL_DEPTH_TEST);    //	need depth test to correctly draw 3D objects 
	glClearColor(0,0,0,1);		
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 

	glm::vec3 tmp = glm::vec3(0.0f, 0.0f, 0.0f);			//	no ambient light
	glUniform3fv(m3, 1, &tmp[0]);
	tmp = glm::vec3(0.6f, 0.6f, 0.6f);			//	diffuse light
	glUniform3fv(m4, 1, &tmp[0]);
	tmp = glm::vec3(1.0f, 1.0f, 1.0f);			//	specular light
	glUniform3fv(m5, 1, &tmp[0]);

	if (!filledOrWired)
		glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); 
	else
		glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); 

	//	set up projection parameters
	projection = glm::perspective(60.0f,1.0f,.1f,100.0f); 
	glUniformMatrix4fv(m0, 1, GL_FALSE, &projection[0][0]); 

	//	set up view parameters
	glm::mat4 view = glm::lookAt(glm::vec3(-5.0, 5.0, 10.0), 
			       glm::vec3(0.0, 0.0, 0.0), 
			       glm::vec3(0.0, 1.0, 0.0)); 

	//	glut transformation parameters
	glm::mat4 model = glm::mat4(1.0f); 
	model = glm::rotate(model, x_angle, glm::vec3(0.0f, 1.0f, 0.0f)); 
	model = glm::rotate(model, y_angle, glm::vec3(1.0f, 0.0f, 0.0f)); 
	model = glm::scale(model,  glm::vec3(scale_size, scale_size, scale_size)); 

	glm::vec4 lightPos = glm::vec4(10.0f, 10.0f, 10.0f, 0.1f);
	lightPos = view * lightPos;
	glUniform3fv(m2, 1, &lightPos[0]);

	// construct the modelview and modelview projection matrices 
	modelview = view * model; 

	//	draw what you want		
	drawFloor();
	drawPly();
	drawStupidMan();
	drawBobbyMan();

	glutSwapBuffers(); 
} 

//////////////////////////////////////////////////////////////////////////////////
//
//    GLUT stuff 
//
void mymouse(int button, int state, int x, int y)
{
	if (state == GLUT_DOWN) 
	{
		press_x = x; press_y = y; 
		if (button == GLUT_LEFT_BUTTON)
			xform_mode = XFORM_ROTATE; 
		else if (button == GLUT_RIGHT_BUTTON) 
			xform_mode = XFORM_SCALE;   
	}
	else if (state == GLUT_UP) 
	{
		xform_mode = XFORM_NONE; 
	}
}
void mymotion(int x, int y)
{
	if (xform_mode==XFORM_ROTATE) 
	{
		x_angle += (x - press_x)/5.0; 
		if (x_angle > 180) x_angle -= 360; 
		else if (x_angle <-180) x_angle += 360; 
		press_x = x; 
	   
		y_angle += (y - press_y)/5.0; 
		if (y_angle > 180) y_angle -= 360; 
		else if (y_angle <-180) y_angle += 360; 
		press_y = y; 
     }
	else if (xform_mode == XFORM_SCALE)
	{
		float old_size = scale_size;
		scale_size *= (1+ (y - press_y)/60.0); 
		if (scale_size <0) scale_size = old_size; 
		press_y = y; 
    }
    glutPostRedisplay(); 
}

//	key event, 'f'-forward, 'b'-backward, 'l'-lift left arm, 'r'-lift right arm, 't'-turn clockwise, 'T'-turn counterclockwise
void mykey(unsigned char key, int x, int y)
{
	switch(key)
	{
	case 'f': 
		if (anim == NONE)
		{
			anim = FORWARD;
			step = 0;
		}
		break;
	case 'b': 
		if (anim == NONE)
		{
			anim = BACKWARD;
			step = 0;
		}
		break;
	case 'l': 
		if (anim == NONE)
		{
			anim = LIFT_LARM;
			step = 0;
		}
		break;
	case 'r': 
		if (anim == NONE)
		{
			anim = LIFT_RARM;
			step = 0;
		}
		break;
	case 't': 
		if (anim == NONE)
		{
			anim = TURN_CLOCK;
			step = 0;
		}
		break;
	case 'T': 
		if (anim == NONE)
		{
			anim = TURN_COUNTER;
			step = 0;
		}
		break;
	case 'w':
		filledOrWired = !filledOrWired;
		glutPostRedisplay(); 
		break;
	}
}

//	animation, processed during idle.
void animate()
{
	switch (anim)
	{
	case FORWARD:
	{
		//	walk forward
		blobbyManModel = glm::translate(blobbyManModel, glm::vec3(0.0, 0.0, 1.0/stepNumber));
		step++;
		glutPostRedisplay(); 
		break;
	}
	case BACKWARD:
	{
		//	walk backward
		blobbyManModel = glm::translate(blobbyManModel, glm::vec3(0.0, 0.0, -1.0/stepNumber));
		step++;
		glutPostRedisplay(); 
		break;
	}
	case TURN_CLOCK:
	{
		//	turn 15 degree clockwise
		blobbyManModel = glm::rotate(blobbyManModel, 15.0f/stepNumber, glm::vec3(0.0, 1.0, 0.0));
		step++;		
		glutPostRedisplay(); 
		break;
	}
	case TURN_COUNTER:
	{
		//	turn 15 degree clockwise
		blobbyManModel = glm::rotate(blobbyManModel, 15.0f/stepNumber, glm::vec3(0.0, -1.0, 0.0));
		step++;	
		glutPostRedisplay(); 
		break;
	}
	case LIFT_LARM:
	{
		//	lift left arm
		step++;
		glutPostRedisplay(); 
		break;		
	}
	case LIFT_RARM:
	{
		//	lift right arm
		step++;
		glutPostRedisplay(); 
		break;
	}
	default:
		step = 0;
		anim = NONE;
	}

	if (step >= stepNumber)
	{
		step = 0;
		anim = NONE;
	}
}

///////////////////////////////////////////////////////////////////////////////
//
int main(int argc, char** argv) 
{ 
	glutInit(&argc, argv); 
	glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH); 
	glutInitWindowSize(600,600); 

	glutCreateWindow("shader cube"); 
	glutDisplayFunc(display); 
	glutMouseFunc(mymouse); 
	glutMotionFunc(mymotion);
	glutKeyboardFunc(mykey);
	glutIdleFunc(animate);

	//	initialize GLEW 
	GLenum err = glewInit(); 
	if ( err != GLEW_OK)  
	{
		printf("Error initializing GLEW! \n"); 
		exit(1);
	}
	
	//	create shaders and get the attribute/uniform locations
	programObject = SetupGLSL("phong");  //create shaders
	glUseProgram(programObject);
	c0 = glGetAttribLocation(programObject, "position"); 
	c1 = glGetAttribLocation(programObject, "normal"); 
	c2 = glGetAttribLocation(programObject, "color"); 
	m0 = glGetUniformLocation(programObject, "projectionMatrix"); 
	m1 = glGetUniformLocation(programObject, "modelViewMatrix");
	m2 = glGetUniformLocation(programObject, "lightPos");
	m3 = glGetUniformLocation(programObject, "ambientIntensity");	
	m4 = glGetUniformLocation(programObject, "diffuseIntensity");	
	m5 = glGetUniformLocation(programObject, "specularIntensity");
	m6 = glGetUniformLocation(programObject, "diffuseMaterial");			
	m7 = glGetUniformLocation(programObject, "specularMaterial");	
	m8 = glGetUniformLocation(programObject, "shiness");	

	glEnableVertexAttribArray(c0); 
	glEnableVertexAttribArray(c1); 
	glEnableVertexAttribArray(c2);

	//	create a PlyFile from a input .ply file
	
	PlyFile* input;
	FILE * pFile;
	printf("please specify your ply file: \n");
	char path[100];
	scanf("%s", path);
	pFile = fopen (path, "r");
	if (!pFile)
	{
		printf("Error opening .ply file! \n"); 
		exit(1);
	}
	
	// get the ply structure
	input = read_ply(pFile);
	if (!input)
	{
		printf("Error creating PlyFile! \n"); 
		exit(1);
	}

	//	create geometry VBOs
	createCube(0.5, 0.8, 0.5, 0.8);
	createCone(0.5, 1.0, 50, 50, 0, 0, 0.5);
	createCylinder(0.5, 0.2, 1.0, 50, 50, 0.5, 0.5, 0.5);
	createSphere(0.5, 50, 50, 0.5, 0.8, 0.5);
	createPly(input);

	glutMainLoop(); 
} 
